import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

/**
 * Starter bot implementation.
 */
public class MyBot extends Bot {
	AntManager manager = new AntManager();
	HashMap<Tile, Aim> orders = new HashMap<Tile, Aim>();
	HashSet<Tile> unseenTiles = null;
	Ants ants;
	MiniRoute guard[] = null;

	/**
	 * Main method executed by the game engine for starting the bot.
	 * 
	 * @param args
	 *            command line arguments
	 * 
	 * @throws IOException
	 *             if an I/O error occurs
	 */
	public static void main(String[] args) throws IOException {
		new MyBot().readSystemInput();
		// Tile a = new Tile(2, 2),
		// b = new Tile(2, 2);
		// HashMap<Tile, Tile> orders = new HashMap<Tile, Tile>();
		// orders.put(a, null);
		// System.out.println(orders.containsKey(b));
	}

	/**
	 * For every ant check every direction in fixed order (N, E, S, W) and move
	 * it if the tile is passable.
	 */
	@Override
	public void doTurn() {
		ants = getAnts();

		orders.clear();
		Ants ants = getAnts();
		ArrayList<Route> routes = new ArrayList<Route>();

		// ArrayList<Tile> hills = new ArrayList<Tile>(ants.getEnemyHills());
		// HashMap<Tile, Tile> foodTargets = new HashMap<Tile, Tile>();

		if (unseenTiles == null)
			initUnseenTiles();

		// verifyGuard();

		// for (Tile myHill : ants.getMyHills())
		// orders.put(myHill, null);// evita ordens que obstrua o formigueiro

		verifyVisiblesTiles(unseenTiles);// Remove os tiles visiveis
		for (Tile ant : ants.getMyAnts())
			for (Tile unsee : unseenTiles) {
				routes.add(new Route(ant, unsee, ants.getDistanceDouble(ant,
						unsee)));
			}
		Collections.sort(routes);
		for (Iterator<Route> iterator = routes.iterator(); iterator.hasNext();) {
			Route route = iterator.next();
			if (!orders.containsKey(route.start)
					&& !orders.containsValue(route.end))
				for (Aim direction : ants.getDirections(route.start, route.end))
					if (ants.getIlk(route.end).isPassable()
							&& ants.getIlk(route.end).isUnoccupied())
						issueOrder(route.start, direction);

		}
	}

	private void initUnseenTiles() {
		unseenTiles = new HashSet<Tile>();
		for (int row = 0; row < ants.getRows(); row++) {
			for (int col = 0; col < ants.getCols(); col++) {
				unseenTiles.add(new Tile(row, col));
			}
		}
	}

	private void initRoutes(ArrayList<Route> routes) {
		for (Tile ant : ants.getMyAnts()) {
			for (Tile food : ants.getFoodTiles()) {
				routes.add(new Route(ant, food, ants.getDistanceDouble(ant,
						food)));
			}// for
		}// for
	}// initRoutes

	private void verifyGuard() {
		if (guard == null && ants.getMyHills().size() != 0) {
			initGuard();
		} else if (ants.getMyHills().size() > 0) {
			for (int i = 0; i < 4; i++) {
				if (ants.getMyAnts().size() > 12
						&& ants.getIlk(guard[i].getNextTile()) == Ilk.MY_ANT)
					orders.put(guard[i].getNextTile(), Aim.EMPTY);
			}
		}
	}

	private void verifyVisiblesTiles(HashSet<Tile> tiles) {
		for (Iterator<Tile> iterator = tiles.iterator(); iterator.hasNext();) {
			Tile next = iterator.next();
			if (ants.isVisible(next))
				iterator.remove();
		}
	}

	/**
	 * Inicia a definicao das podicoes cocupadas pelas formigas que farao a
	 * </br> guarda do formigueiro.
	 */
	private void initGuard() {
		Iterator<Tile> i = ants.getMyHills().iterator();
		Tile myHill = i.next();
		guard = new MiniRoute[4];
		Tile startEnd = new Tile(myHill.getRow() - 2, myHill.getCol() - 2);
		guard[0] = new MiniRoute(startEnd, startEnd);
		startEnd = new Tile(myHill.getRow() - 2, myHill.getCol() + 2);
		guard[1] = new MiniRoute(startEnd, startEnd);
		startEnd = new Tile(myHill.getRow() + 2, myHill.getCol() + 2);
		guard[2] = new MiniRoute(startEnd, startEnd);
		startEnd = new Tile(myHill.getRow() + 2, myHill.getCol() - 2);
		guard[3] = new MiniRoute(startEnd, startEnd);

		startEnd = new Tile(myHill.getRow() - 1, myHill.getCol());
		guard[3] = new MiniRoute(startEnd, startEnd);
		startEnd = new Tile(myHill.getRow() + 1, myHill.getCol());
		guard[3] = new MiniRoute(startEnd, startEnd);

	}

	/**
	 * Todas formigas seguem em direcaoo do primeiro formigueiro encontrado
	 * 
	 * @param ants
	 * @return
	 */
	boolean hunterHill(Ants ants) {
		for (Tile ant : ants.getMyAnts()) {
			if (ants.getEnemyHills().size() > 0)
				for (Tile hill : ants.getEnemyHills()) {
					doMove(ant, hill);
					break;
				}// for
		}
		return false;
	}

	/**
	 * Todas as formigas seguem em direcao a comida mas proxima
	 * 
	 * @param ants
	 * @return
	 */
	boolean hunterFood(Ants ants) {
		Tile minFood = null;
		for (Tile ant : ants.getMyAnts()) {
			if (ants.getFoodTiles().size() > 0) {
				for (Tile food : ants.getFoodTiles()) {
					// if(ants.getDistance(ant, food)<30){
					// seekAndDestroy(ant, food, ants);
					// continue;
					// }
					if (minFood == null
							|| ants.getDistance(ant, food) < ants.getDistance(
									ant, minFood))
						minFood = food;
				}// for to select the minimal distance
				if (minFood != null)
					doMove(ant, minFood);
				else
					doMoveDirection(ant, getRamdomDirection());
			} else {
				doMoveDirection(ant, getRamdomDirection());
			}// if ants.size() = 0
		}// for ants
		return false;
	}

	boolean hunterFood(Tile ant) {
		Tile minFood = null;
		if (ants.getFoodTiles().size() > 0) {
			for (Tile food : ants.getFoodTiles()) {
				if (minFood == null
						|| ants.getDistance(ant, food) < ants.getDistance(ant,
								minFood))
					minFood = food;
			}// for to select the minimal distance
			if (minFood != null)
				doMove(ant, minFood);
			else
				doMoveDirection(ant, getRamdomDirection());
		} else {
			doMoveDirection(ant, getRamdomDirection());
		}// if ants.size() = 0
		return false;
	}

	/**
	 * Retorna uma direcao randomica
	 * 
	 * @return
	 */
	Aim getRamdomDirection() {
		int direction = (int) (Math.random() * 4);
		switch (direction) {
		case 0:
			return Aim.EAST;
		case 1:
			return Aim.WEST;
		case 2:
			return Aim.SOUTH;
		case 3:
			return Aim.NORTH;
		default:
			return Aim.SOUTH;
		}
	}

	private void exploreMap(Tile ant) {
		try {
			ArrayList<Route> routes = new ArrayList<Route>();
			int iterations = 0, maxIterations = 200;
			for (Tile unseen : unseenTiles) {
				int distance = ants.getDistance(ant, unseen);
				routes.add(new Route(ant, unseen, distance));
				if (++iterations >= maxIterations)
					break;
			}
			Collections.sort(routes);
			if (routes.size() > 0)
				doMove(ant, routes.get(0).end);
		} catch (Exception e) {

		}
	}

	private void exploreMap() {
		try {
			HashMap<Tile, Tile> targets = new HashMap<Tile, Tile>();
			ArrayList<Route> routes = new ArrayList<Route>();
			int iterations = 0, maxIterations = 1200;
			for (Tile ant : ants.getMyAnts())
				if (orders.get(ant) == null)
					for (Tile unseen : unseenTiles) {
						int distance = ants.getDistance(ant, unseen);
						routes.add(new Route(ant, unseen, distance));
						if (++iterations >= maxIterations)
							break;
					}
			Collections.sort(routes);
			for (Route route : routes)
				if (targets.get(route.start) == null
				// && !targets.containsValue(route.end)
						&& doMove(route.start, route.end))
					targets.put(route.start, route.end);

		} catch (Exception e) {

		}
	}

	boolean doMove(Tile ant, Tile target) {
		for (Aim direction : getAnts().getDirections(ant, target))
			if (getAnts().getIlk(ant, direction).isPassable()
					&& getAnts().getIlk(ant, direction).isUnoccupied()
					&& orders.get(ant) == null) {
				try {
					getAnts().setIlk(getAnts().getTile(ant, direction),
							Ilk.IMPAIRED);
					issueOrder(ant, direction);
					return true;
				} catch (Exception e) {

				}
			}
		return false;
	}

	void seekAndDestroy(Tile ant, Tile target, Ants ants) {
		Route route = new Route(ant, target, ants
				.getDistanceDouble(ant, target));
		doMoveDirection(ant, route.getDirection(ants));
	}

	/**
	 * Verifica disponibilidade para o proximo movimento , caso o proximo tile
	 * esteja disponivel </br> movimenta a formiga e retorna <code>true</code>
	 * 
	 * @param myAnt
	 *            Tile que representa a formiga analizada
	 * @param direction
	 *            objeto Aim que representa a direo para a qual se deseja seguir
	 * @return
	 */
	boolean doMoveDirection(Tile myAnt, Aim direction) {
		Ants ants = getAnts();
		Tile nextTile = ants.getTile(myAnt, direction);
		if (ants.getIlk(nextTile).isPassable()
				&& ants.getIlk(nextTile).isUnoccupied()
				&& orders.get(myAnt) == null) {
			try {
				ants.setIlk(nextTile, Ilk.IMPAIRED);
				issueOrder(myAnt, direction);
				return true;
			} catch (Exception e) {
				return false;
			}
		} else
			return false;
	}

	void issueOrder(Tile ant, Aim direction) {
		Logger.log("Ordem : " + ant + " - " + direction);
		orders.put(ant, direction);
		ants.issueOrder(ant, direction);
	}
}

class MiniRoute {
	Tile start, end;

	public MiniRoute(Tile start, Tile end) {
		this.start = start;
		this.end = end;
	}

	public Tile getNextTile() {
		return end;
	}
}
